package org.javaexpert.junit4;

import org.junit.internal.runners.InitializationError;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.manipulation.NoTestsRemainException;
import org.junit.runner.notification.RunNotifier;

import java.util.Arrays;
import static java.util.Arrays.asList;
import java.util.HashSet;
import java.util.Set;

/**
 * Created by IntelliJ IDEA.
 * User: piotrga
 * Date: Oct 17, 2007
 * Time: 9:26:34 PM
 * To change this template use File | Settings | File Templates.
 */
public class ExtendedRunner extends Runner {

    private Set<String> includeGroups;
    private Set<String> excludeGroups;
    private Runner delegateRunner;

    public ExtendedRunner(Class<?> klass) throws InitializationError {
        initializeDelegateTo(klass, initializeFilter(klass));
    }

    private MethodFilter initializeFilter(Class<?> klass) {
        includeGroups = new HashSet<String>();
        excludeGroups = new HashSet<String>();
        includeGroups.addAll(asList(System.getProperty("extendedrunner.groups.include", "all").split(",")));
        excludeGroups.addAll(asList(System.getProperty("extendedrunner.groups.exclude", "").split(",")));
        return new MethodFilter(includeGroups, excludeGroups, classLevelGroupsPresent(klass));
    }

    private HashSet classLevelGroupsPresent(Class<?> klass) {
        return new HashSet(Arrays.asList(klass.isAnnotationPresent(TestGroups.class) ? klass.getAnnotation(TestGroups.class).value() : new String[0]));
    }

    private void initializeDelegateTo(Class<?> klass, MethodFilter filter) throws InitializationError {
        DelegateToRunner delegateToAnnotation = klass.getAnnotation(DelegateToRunner.class);
        if (delegateToAnnotation == null) {
            delegateRunner = new JUnit4ClassRunner(klass);
        }else{
            try {
                delegateRunner = delegateToAnnotation.value().getConstructor(Class.class).newInstance(klass);
            }  catch (Exception e) {
                throw new InitializationError(String.format("Can not instantiate delegate runner %s. Couse: %s", delegateToAnnotation.value(), e.getMessage()));
            }
        }
        try {
            filter.apply(delegateRunner);
        } catch (NoTestsRemainException e) {
            System.out.printf("No tests remaining after applying filter %s to class %s\n", filter.describe(), klass);
        }
    }

    public Description getDescription() {
        return delegateRunner.getDescription();
    }

    public void run(RunNotifier notifier) {
        delegateRunner.run(notifier);
    }
}
